package cc.magicjson.data.operation.router.db.service.impl;


import cc.magicjson.data.operation.router.db.exception.DatabaseOperationException;
import cc.magicjson.data.operation.router.db.executor.EntityOperationExecutor;
import cc.magicjson.data.operation.router.db.router.OperationRouter;
import cc.magicjson.data.operation.router.db.service.DataOperationService;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.actuate.endpoint.OperationType;
import org.springframework.context.ApplicationContext;

import org.springframework.stereotype.Service;

import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.util.List;

@Service

public class DefaultDataOperationService implements DataOperationService {
    private final OperationRouter router;
    private final EntityOperationExecutor executor;
    private final ApplicationContext applicationContext;

    public DefaultDataOperationService(OperationRouter router, EntityOperationExecutor executor, ApplicationContext applicationContext) {
        this.router = router;
        this.executor = executor;
        this.applicationContext = applicationContext;
    }

    @Override
    public <T> T save(T entity, Class<T> entityClass) throws DatabaseOperationException {
        OperationType opType = router.determineOperationType(getCurrentMethod());
        List<DataSource> dataSources = router.determineTargetDataSources(opType);

        return executor.executeWrite(entity, dataSources, (ds, e) -> {
            // 根据实体类型获取对应的Repository或Mapper
            Object repository = getRepository(entityClass, ds);
            return invokeRepositoryMethod(repository, "save", e);
        });
    }

    @Override
    public <T> void delete(T entity, Class<T> entityClass) {

    }

    @Override
    public <T> T update(T entity, Class<T> entityClass) {
        return null;
    }

    @Override
    public <T> List<T> batchSave(List<T> entities, Class<T> entityClass) {
        return List.of();
    }

    @Override
    public <T> void batchDelete(List<T> entities, Class<T> entityClass) {

    }

    @Override
    public <T> List<T> batchUpdate(List<T> entities, Class<T> entityClass) {
        return List.of();
    }

    @Override
    public <T> T findById(Object id, Class<T> entityClass) {
        return null;
    }

    @Override
    public <T> List<T> findByCondition(Object condition, Class<T> entityClass) {
        return List.of();
    }

    protected Method getCurrentMethod() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        for (StackTraceElement element : stackTrace) {
            try {
                Class<?> clazz = Class.forName(element.getClassName());
                if (DataOperationService.class.isAssignableFrom(clazz)) {
                    return clazz.getDeclaredMethod(element.getMethodName());
                }
            } catch (Exception e) {
                // Skip if method not found
            }
        }
        throw new IllegalStateException("Could not determine current method");
    }

    protected <T> Object getRepository(Class<T> entityClass, DataSource dataSource) {
        String repositoryBeanName = entityClass.getSimpleName() + "Repository";
        try {
            return applicationContext.getBean(repositoryBeanName);
        } catch (Exception e) {
            throw new IllegalStateException("Repository not found for entity: " + entityClass.getName(), e);
        }
    }

    protected <T> T invokeRepositoryMethod(Object repository, String methodName, Object... args) {
        try {
            Method method = findMethod(repository.getClass(), methodName, args);
            if (method == null) {
                throw new IllegalStateException("Method " + methodName + " not found in repository");
            }
            @SuppressWarnings("unchecked")
            T result = (T) method.invoke(repository, args);
            return result;
        } catch (Exception e) {
            throw new RuntimeException("Failed to invoke repository method: " + methodName, e);
        }
    }

    private Method findMethod(Class<?> clazz, String methodName, Object... args) {
        for (Method method : clazz.getMethods()) {
            if (method.getName().equals(methodName) && method.getParameterCount() == args.length) {
                return method;
            }
        }
        return null;
    }
}
